﻿using System;
using System.Runtime.InteropServices;

namespace UtilZ.Dotnet.Ex.Model
{
#pragma warning disable 8500

    /// <summary>
    /// 通用类型数据片
    /// </summary>
    unsafe public class PtrSpan<T> where T : struct
    {
        private T* _originDataPtr = null;

        private T* _dataPtr = null;
        /// <summary>
        /// 数据指针
        /// </summary>
        public T* DataPtr
        {
            get
            {
                return this._dataPtr;
            }
        }

        private int _length = 0;
        /// <summary>
        /// 相对于数组或原始数据指针数据起始偏移位置
        /// </summary>
        public int Length
        {
            get
            {
                return this._length;
            }
        }




        /// <summary>
        /// 数据容量
        /// </summary>
        private int _capcity = 0;
        /// <summary>
        /// 数据容量
        /// </summary>
        public int Capcity
        {
            get
            {
                return this._capcity;
            }
        }


        /// <summary>
        /// 获取或设置用户定义的数据
        /// </summary>
        public object Tag { get; set; }


        /// <summary>
        /// 获取或设置值
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        /// <exception cref="IndexOutOfRangeException">索引越界异常</exception>
        public T this[int index]
        {
            get
            {
                if (index < 0 || index >= this._length)
                {
                    throw new IndexOutOfRangeException($"索引越界,index:{index},当前数据长度:{this._length}");
                }

                return *(this._dataPtr + index);
            }
            set
            {
                if (index < 0 || index >= this._length)
                {
                    throw new IndexOutOfRangeException($"索引越界,index:{index},当前数据长度:{this._length}");
                }

                *(this._dataPtr + index) = value;
            }
        }


        /// <summary>
        /// 构造函数
        /// </summary>
        public PtrSpan()
        {

        }

        /// <summary>
        /// 构造函数-集合数据片
        /// </summary>
        /// <param name="dataPtr">数据指针</param>
        /// <param name="length">数据长度</param>
        /// <param name="capcity">指针容量</param>
        public PtrSpan(T* dataPtr, int length, int capcity)
        {
            this.Update(dataPtr, length, capcity);
        }



        /// <summary>
        /// 更新数据片
        /// </summary>
        /// <param name="startIndex">数据起始偏移位置,小于等于0从头开始</param>
        /// <param name="length">数据长度，小于0到结尾</param>
        public void Update(int startIndex, int length)
        {
            if (startIndex < 0 || startIndex >= this._capcity)
            {
                throw new ArgumentOutOfRangeException($"数据起始偏移位置不能小于0且不能大于等于容量值,startIndex无效");
            }

            if (length < 0 || length > this._capcity)
            {
                throw new ArgumentOutOfRangeException($"长度值不能小于0且不能超出容量上限,length无效,");
            }

            if (startIndex + length > this._capcity)
            {
                throw new ArgumentOutOfRangeException($"数据长度超出容量值.");
            }

            this._dataPtr = this._originDataPtr + startIndex;
            this._length = length;
        }

        /// <summary>
        /// 更新数据片
        /// </summary>
        /// <param name="dataPtr">数据指针</param>
        /// <param name="length">数据长度</param>
        /// <param name="capcity">指针容量</param>
        public void Update(T* dataPtr, int length, int capcity)
        {
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException($"长度值\"{length}\"无效,超出范围,不能小于0");
            }

            if (capcity < length)
            {
                throw new ArgumentOutOfRangeException($"指针容量不能小于数据长度值\"{length}\"");
            }

            this._dataPtr = dataPtr;
            this._originDataPtr = dataPtr;
            this._length = length;
            this._capcity = capcity;
        }

        /// <summary>
        /// 获取第一项
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public T First()
        {
            return this._dataPtr[0];
        }

        /// <summary>
        /// 获取最后一项
        /// </summary>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public T Last()
        {
            var index = this._length - 1;
            return this._dataPtr[index];
        }

        /// <summary>
        /// 获取第一项或默认值
        /// </summary>
        /// <returns>第一项或默认值</returns>
        public T FirstOrDefault()
        {
            if (this._dataPtr == null || this._length <= 0)
            {
                return default(T);
            }
            else
            {
                return this._dataPtr[0];
            }
        }

        /// <summary>
        /// 获取最后一项或默认值
        /// </summary>
        /// <returns>最后一项或默认值</returns>
        public T LastOrDefault()
        {
            if (this._dataPtr == null || this._length <= 0)
            {
                return default(T);
            }
            else
            {
                return this._dataPtr[this._length - 1];
            }
        }





        /// <summary>
        /// 克隆当前对象,数据重新复制一份
        /// </summary>
        /// <returns>克隆完的数据</returns>
        public T[] ToArray()
        {
            if (this._length == 0)
            {
#if NET4_0
                return Base.ArrayEx.Empty<T>();
#else
                return System.Array.Empty<T>();
#endif
            }

            T[] array = new T[this._length];
            fixed (T* ptr = array)
            {
                var byteCount = array.Length * sizeof(T);
                NativeMemory.Copy(ptr, this._dataPtr, (nuint)byteCount);
            }

            return array;
        }

        /// <summary>
        /// 获取Span数据
        /// </summary>
        /// <returns>Span数据</returns>
        public Span<T> GetSpan()
        {
            return new Span<T>(this._dataPtr, this._length);
        }

        /// <summary>
        /// 获取SpanZ数据
        /// </summary>
        /// <returns>Span数据</returns>
        public SpanZ<T> GetSpanZ()
        {
            return new SpanZ<T>(this._dataPtr, this._length);
        }


    }

}
